package com.ydj.framework.mysql;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ydj.framework.protocol.BaseEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * Mysql查询的基类
 *
 * @author rick
 */
public class BaseQuery<T extends BaseEntity> {
    @Getter
    private ExtLambdaQueryWrapper<T> lambdaQueryWrapper;

    private IService<T> service;

    private String limit;

    private String forUpdate;

    @Setter
    @Accessors(chain = true)
    private boolean errorSql = false;

    public BaseQuery(IService<T> service) {
        this.service = service;
        this.lambdaQueryWrapper = new ExtLambdaQueryWrapper<>();
    }

    public BaseQuery(IService<T> service, ExtLambdaQueryWrapper<T> extLambdaQueryWrapper) {
        this.service = service;
        this.lambdaQueryWrapper = extLambdaQueryWrapper;
    }

    public T single() {
        last();
        return service.getOne(getQueryWrapper());
    }

    public List<T> list() {
        last();
        return service.list(getQueryWrapper());
    }

    public List<T> list(LambdaQueryWrapper<T> queryWrapper) {
        return service.list(queryWrapper);
    }

    public Map<Long, T> map() {
        return list().stream().collect(Collectors.toMap(T::getId, item->item));
    }

    public Set<Long> set() {
        return list().stream()
                .map(item->item.getId())
                .collect(Collectors.toSet());
    }

    public List<T> page(PageArgs pageArgs) {
        return page(getQueryWrapper(), pageArgs);
    }

    // 这个函数，其实是为了共享 page args这一段
    public List<T> page(LambdaQueryWrapper<T> queryWrapper, PageArgs pageArgs) {
        last();
        Page<T> page = new Page<>(pageArgs.getPage(), pageArgs.getPageSize());
        Page<T> list = service.page(page, queryWrapper);
        pageArgs.setTotalSize(page.getTotal())
                .setPageSize(page.getSize())
                .setPage(page.getCurrent())
                .setTotalPage(page.getPages());
        return list.getRecords();
    }

    @SafeVarargs
    public final BaseQuery<T> select(SFunction<T, ?>... columns) {
        lambdaQueryWrapper.select(columns);
        return this;
    }

    // 这个函数，其实是为了共享 page args这一段
    public List<Map<String, Object>> pageMap(LambdaQueryWrapper<T> queryWrapper, PageArgs pageArgs) {
        last();
        IPage<Map<String, Object>> page = new Page<>(pageArgs.getPage(), pageArgs.getPageSize());
        IPage<Map<String, Object>> list = service.pageMaps(page, queryWrapper);

        pageArgs.setTotalSize(page.getTotal())
                .setPageSize(page.getSize())
                .setPage(page.getCurrent())
                .setTotalPage(page.getPages());

        return list.getRecords();
    }


    public long count() {
        return count(getQueryWrapper());
    }

    public long count(LambdaQueryWrapper<T> queryWrapper) {
        return service.count(queryWrapper);
    }

    private LambdaQueryWrapper<T> getQueryWrapper() {
        if (errorSql) {
            return new QueryWrapper<T>().eq("1", "0").lambda();
        }
        return lambdaQueryWrapper;
    }

    private boolean isNotNull(Object val) {
        if (val == null) {
            return false;
        }
        if (val instanceof String) {
            return StringUtils.isNotBlank((String) val);
        }
        return true;
    }

    protected void eq(String column, Object val) {
        lambdaQueryWrapper.eq(isNotNull(val), column, val);
    }

    protected void eq(SFunction<T, ?> column, Object val) {
        lambdaQueryWrapper.eq(isNotNull(val), column, val);
    }

    protected void like(SFunction<T, ?> column, Object val) {
        lambdaQueryWrapper.like(isNotNull(val), column, val);
    }

    protected void likeRight(SFunction<T, ?> column, Object val) {
        lambdaQueryWrapper.likeRight(isNotNull(val), column, val);
    }

    protected void lt(SFunction<T, ?> column, Object val) {
        lambdaQueryWrapper.lt(isNotNull(val), column, val);
    }

    protected void le(SFunction<T, ?> column, Object val) {
        lambdaQueryWrapper.le(isNotNull(val), column, val);
    }

    protected void gt(SFunction<T, ?> column, Object val) {
        lambdaQueryWrapper.gt(isNotNull(val), column, val);
    }

    protected void ge(SFunction<T, ?> column, Object val) {
        lambdaQueryWrapper.ge(isNotNull(val), column, val);
    }

    protected void in(SFunction<T, ?> column, List values) {
        if (values == null || values.isEmpty()) {
            errorSql = true;
        } else if (values.size() == 1) {
            eq(column, values.get(0));
        } else {
            lambdaQueryWrapper.in(true, column, values.toArray());
        }
    }

    protected void in(SFunction<T, ?> column, Set values) {
        if (values == null || values.isEmpty()) {
            errorSql = true;
        } else if (values.size() == 1) {
            eq(column, values.toArray()[0]);
        } else {
            lambdaQueryWrapper.in(true, column, values.toArray());
        }
    }

    protected void orLike(SFunction<T, ?> column, Object[] values){
        if (values == null || values.length == 0) {
            errorSql = true;
        } else if (values.length == 1) {
            eq(column, values[0]);
        } else {
            this.lambdaQueryWrapper.and(true, new Consumer<LambdaQueryWrapper<T>>() {
                @Override
                public void accept(LambdaQueryWrapper<T> tLambdaQueryWrapper) {
                    for (Object obj:values){
                        tLambdaQueryWrapper.like(true,column,obj).or();
                    }
                }
            });
        }
    }

    protected void in(SFunction<T, ?> column, Object[] values) {
        if (values == null || values.length == 0) {
            errorSql = true;
        } else if (values.length == 1) {
            eq(column, values[0]);
        } else {
            lambdaQueryWrapper.in(true, column, values);
        }
    }

	protected void notIn(SFunction<T, ?> column, List values) {
		if (values != null && values.size() > 0) {
			lambdaQueryWrapper.notIn(true, column, values.toArray());
		} else {
			errorSql = true;
		}
	}

	protected void notIn(SFunction<T, ?> column, Object[] values) {
		if (values != null && values.length > 0) {
			lambdaQueryWrapper.notIn(true, column, values);
		} else {
			errorSql = true;
		}
	}

    protected void between(SFunction<T, ?> column, Object from, Object to) {
        boolean fromNotNull = isNotNull(from);
        boolean toNotNull = isNotNull(to);
        if (fromNotNull && toNotNull) {
            lambdaQueryWrapper.between(true, column, from, to);
        } else if (fromNotNull) {
            lambdaQueryWrapper.ge(column, from);
        } else if (toNotNull) {
            lambdaQueryWrapper.le(column, to);
        }
    }

    protected void orderBy(SFunction<T, ?> column, MysqlSort sort) {
        lambdaQueryWrapper.orderBy(true, sort == MysqlSort.ASC, column);
    }

    protected void orderBy(SFunction<T, ?> column, MysqlSort sort, int[] field) {
        lambdaQueryWrapper.orderByWithField(true, sort == MysqlSort.ASC, column, field);
    }

    protected void orderBy(SFunction<T, ?> column, MysqlSort sort, BaseEnum[] field) {
        List<Integer> codeList = new LinkedList<>();
        for (BaseEnum f : field) {
            codeList.add(f.getValue());
        }
        int[] codeArray = codeList.stream().mapToInt(Integer::valueOf).toArray();
        lambdaQueryWrapper.orderByWithField(true, sort == MysqlSort.ASC, column, codeArray);
    }

    protected void orderBy(SFunction<T, ?> column, MysqlSort sort, String[] field) {
        lambdaQueryWrapper.orderByWithField(true, sort == MysqlSort.ASC, column, field);
    }

    protected void ne(SFunction<T, ?> column, Object values) {
        lambdaQueryWrapper.ne(column, values);
    }

	protected void isNull(SFunction<T, ?> column) {
		lambdaQueryWrapper.isNull(column);
	}

    protected void isNotNull(SFunction<T, ?> column) {
        lambdaQueryWrapper.isNotNull(column);
    }

    public BaseQuery<T> limit(int rows) {
        this.limit = "limit " + rows;
        return this;
    }

    public BaseQuery<T> limit(int offset, int rows) {
        this.limit = String.format("limit %d,%d", offset, rows);
        return this;
    }

    public BaseQuery<T> forUpdate() {
        this.forUpdate = "for update";
        return this;
    }

    public BaseQuery<T> forUpdate(boolean condition) {
        if(condition) {
            this.forUpdate = "for update";
        }
        return this;
    }

    private void last() {
        String last = "";
        if (limit != null) {
            last += " " + limit;
        }
        if (forUpdate != null) {
            last += " " + forUpdate;
        }
        lambdaQueryWrapper.last(last);
    }

    /**
     * 用于查询json队列
     */
    public BaseQuery<T> jsonContains(SFunction<T, ?> column, Object objValue) {
        if (objValue == null) {
            return this;
        }
        String columnString = lambdaQueryWrapper.columnToString(column);
        if (objValue instanceof Number) {
            lambdaQueryWrapper.apply(String.format("JSON_CONTAINS(%s, '%s')", columnString, objValue));
        } else {
            lambdaQueryWrapper.apply(String.format("JSON_CONTAINS(%s, '\"%s\"')", columnString, objValue));
        }
        return this;
    }

    public void remove() {
        service.remove(lambdaQueryWrapper);
    }
}